Bad weather Kelheim Demo

Oleksandr Soboliev

Research Meteostat

After some researches about meteostat data nearest station in DE that belongs to Kelheim region are:

there are many of them so I am starting to think about extracting all from the Bayern or extract the nearest from longtitude/latitude point with the Kelheim shapefile(using json and Euclid distances)

Kelheim has no weather station, but it could be reconstructed with 2 other

Hohenfels with id: “10775” and Ingolstadt with id:“10860” kelheim_data = {weight1}x{hohenfels} + {weight2}x{inglstadt}

Also this site shows, that there are many of the Kelheim stations in this area, but meteostat doesn’t contain them https://www.wunderground.com/dashboard/pws/IKELHE5

Research Weatherstack

weatherstack_kelheim = read_delim("data/Kelheim_weather_since_july_2008.csv",delim = ",")
Rows: 120312 Columns: 6── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): description
dbl  (4): hour, precip, visibility, totalsnow_daily
date (1): date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(weatherstack_kelheim)

What to take as a reffer point isn’t clear because of the date(before/after covid) and weather type (sunny,clear,temperature) Also there is no temperature in it :/

Import mobility from Google

#global_mobility = read_delim("https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv",",")
#de_mobility = global_mobility %>% filter(country_region_code == "DE")
#print(unique(de_mobility$sub_region_1))

As we can see the most precise region to filter data from is Bavaria :/

Relevant data for the , mobility

#bavaria_mobility = de_mobility %>% filter(sub_region_1 == "Bavaria")
#bavaria_mobility = bavaria_mobility %>% #dplyr::select(country_region,sub_region_1,date,residential_percent_change_from_baseline) %>%
#  mutate(residential_percent_change_from_baseline = -residential_percent_change_from_baseline,
#         source = "Google")%>%
#  rename(BundeslandID = sub_region_1,not_at_home_change = residential_percent_change_from_baseline)
#bavaria_mobility = bavaria_mobility %>% dplyr::select(date,BundeslandID,not_at_home_change,source)
#Need to filter out weekends

#plt = ggplot(bavaria_mobility)+
#  geom_point(aes(x = date,y = not_at_home_change))
#ggplotly(plt)

Import mobility from Senozon

snz_mobility = read_delim("data/LK_mobilityData_weekdays.csv",";")
Rows: 53064 Columns: 4── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ";"
chr (1): Landkreis
dbl (3): date, outOfHomeDuration, percentageChangeComparedToBeforeCorona
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Kelheim
snz_mobility_kelheim = snz_mobility %>% filter(Landkreis == "Landkreis Kelheim") %>% mutate(source = "senozon") %>% dplyr::select(-outOfHomeDuration) %>% rename(not_at_home_change = percentageChangeComparedToBeforeCorona)
snz_mobility_kelheim$date = as.Date(strptime(snz_mobility_kelheim$date,"%Y%m%d"))

#Berlin
snz_mobility_berlin = snz_mobility %>% filter(Landkreis == "Berlin") %>% mutate(source = "senozon") %>% dplyr::select(-outOfHomeDuration) %>% rename(not_at_home_change = percentageChangeComparedToBeforeCorona)
snz_mobility_berlin$date = as.Date(strptime(snz_mobility_berlin$date,"%Y%m%d"))

colors <- c("Berlin" = "blue", "Kelheim" = "red")

plt = ggplot()+
  geom_point(data = snz_mobility_kelheim,aes(x = date,y = not_at_home_change,color = "Berlin"))+
  geom_point(data = snz_mobility_berlin,aes(x = date,y = not_at_home_change,color = "Kelheim"))+
  scale_colour_manual(values = colors)
ggplotly(plt)

Aggregate 2 sources

We take berlin weather from station in Schoenefeld with id = 10384

Rows: 24479 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (10): X2, X3, X4, X5, X6, X7, X8, X9, X10, X11
date  (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# think about duration of description column
# Kelheim
weatherstack_kelheim_daily = weatherstack_kelheim %>%
  group_by(date) %>%
  summarize(description = description,precip_day = sum(precip),visibility_mean = mean(visibility),totalsnow_daily = mean(totalsnow_daily))
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.
  
weatherstack_kelheim_weekly = weatherstack_kelheim_daily %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(description = description,date = first(date), precip_week = sum(precip_day),visibility_mean = mean(visibility_mean),totalsnow_weekly =sum( totalsnow_daily))
`summarise()` has grouped output by 'year_week'. You can override using the `.groups` argument.
weatherstack_kelheim_weekly = unique(weatherstack_kelheim_weekly)
print(weatherstack_kelheim_weekly)

#Berlin
  
berlin_weather_weekly = berlin_weather_daily %>% filter(year(date) >=2020) %>%
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date), prcp_week = sum(prcp), tavg= mean(tavg),snow_week =sum( snow),wspd = mean(wspd),tmax = max(tmax)) %>%
  arrange(year_week)
print(berlin_weather_weekly)
NA
#mob_joined = rbind(snz_mobility_kelheim,bavaria_mobility)
#Kelheim
snz_mobility_kelheim_year_week = snz_mobility_kelheim %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date),not_at_home_change = mean(not_at_home_change))
mob_joined_with_weather_kelheim = snz_mobility_kelheim_year_week %>% inner_join(weatherstack_kelheim_weekly, by = "year_week") %>% dplyr::select(-date.y) %>% rename(date = date.x)
print(mob_joined_with_weather_kelheim)

#Berlin
snz_mobility_berlin_year_week = snz_mobility_berlin %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date),not_at_home_change = mean(not_at_home_change))
mob_joined_with_weather_berlin = snz_mobility_berlin_year_week %>% inner_join(berlin_weather_weekly, by = "year_week") %>% dplyr::select(-date.y) %>% rename(date = date.x)
print(mob_joined_with_weather_berlin)
#First plot with colour as precipitation
shapes <- c("Berlin" = 5, "Kelheim" = 3)
plt_color = ggplot()+
  geom_point(data = mob_joined_with_weather_kelheim,aes(x = date,y = not_at_home_change,colour = precip_week,shape = "Kelheim"))+
  #geom_point(data = mob_joined_with_weather_berlin,aes(x = date,y = not_at_home_change,colour = prcp_week,shape = "Berlin"))+
  scale_color_gradient2()+
  scale_shape_manual(values = shapes)
  

ggplotly(plt_color)
#Second plot as another line as precipitation
plt_line = ggplot(mob_joined_with_weather_kelheim)+
  geom_point(aes(x = date,y = not_at_home_change))+
  geom_line(aes(x = date,y = precip_week*0.5,color = "red"))
  

ggplotly(plt_line)
plt_hist_precip = ggplot(mob_joined_with_weather_kelheim,aes(x = precip_week,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,fill = "blue")

ggplotly(plt_hist_precip)
plt_hist_visibility = ggplot(mob_joined_with_weather_kelheim,aes(x = visibility_mean,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 0.5,fill = "blue")

ggplotly(plt_hist_visibility)
plt_hist_totalsnow = ggplot(mob_joined_with_weather_kelheim,aes(x = totalsnow_weekly,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 7,fill = "blue")

ggplotly(plt_hist_totalsnow)
#this is a bad plot because it takes description of 1 day of the week
plt_hist_descr = ggplot(mob_joined_with_weather_kelheim,aes(x = description,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 5,fill = "blue")+
  coord_flip()

ggplotly(plt_hist_descr)

Let’s try it out with meteostat data, that contains scope of the 2022 without restictions

Ingolstadt data from id = 10860 station

ingolstadt_weather = read_delim("https://bulk.meteostat.net/v2/daily/10860.csv.gz",",",col_names = FALSE)
Rows: 19346 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (9): X2, X3, X4, X5, X6, X7, X8, X9, X10
lgl  (1): X11
date (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
colnames(ingolstadt_weather) = c("date", "tavg", "tmin", "tmax", "prcp", "snow", "wdir", "wspd", "wpgt", "pres", "tsun")


# We don't need data of weather before 2020, because of snz_mobility date, also data isn't precise

ingolstadt_weather = ingolstadt_weather %>% filter(year(date)>=2020)%>% replace_na(list(snow = 0))

print(ingolstadt_weather)

Hohenfels data from id = 10775 station

hohenfels_weather = read_delim("https://bulk.meteostat.net/v2/daily/10775.csv.gz",",",col_names = FALSE)
Rows: 6367 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (8): X2, X3, X4, X5, X6, X7, X8, X10
lgl  (2): X9, X11
date (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
colnames(hohenfels_weather) = c("date", "tavg", "tmin", "tmax", "prcp", "snow", "wdir", "wspd", "wpgt", "pres", "tsun")


# We don't need data of weather before 2020, because of snz_mobility date, also data isn't precise

hohenfels_weather = hohenfels_weather %>% filter(year(date)>=2020) %>% replace_na(list(snow = 0))

print(hohenfels_weather)

As we can see in Hohenfels data isn’t that accurate and precipitation is data is missing fr year 2020, so for the further analysis we take only Ingolstadt data.

ingolstadt_weather_weekly = ingolstadt_weather %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date), prcp_week = sum(prcp), tavg= mean(tavg),snow_week =sum( snow),wspd = mean(wspd),tmax = max(tmax)) %>%
  arrange(year_week)
#ingolstadt_weather_weekly = unique(weatherstack_kelheim_weekly)
print(ingolstadt_weather_weekly)
mob_joined_with_ingolstadt = ingolstadt_weather_weekly %>% 
  inner_join(snz_mobility_kelheim_year_week, by = "year_week") %>% 
  dplyr::select(-date.x) %>%
  rename(date = date.y) %>%
  replace_na(list(tmax = 0))

print(mob_joined_with_ingolstadt)
#First plot with colour as precipitation
fills <- c("Ingolstadt" = "blue", "Berlin" = "red")
plt_ing_color = ggplot(mob_joined_with_ingolstadt)+
  geom_point(aes(x = date,y = not_at_home_change,colour = prcp_week,fill = "Ingolstadt"))+
  geom_point(data = mob_joined_with_weather_berlin,aes(x = date,y = not_at_home_change,colour = prcp_week,fill = "Berlin"))+
  scale_color_gradient(low = "white",high = "black")+
  scale_fill_manual(values = fills)

ggplotly(plt_ing_color)
plt_ing_color = ggplot(mob_joined_with_ingolstadt)+
  geom_point(aes(x = date,y = not_at_home_change))+
  geom_line(aes(x = date,y = prcp_week,color = "Ingolstadt"))+
  #geom_line(data = berlin_weather_weekly,aes(x = date,y = prcp_week,color = "Berlin"))+
  scale_color_manual(values = fills)+
  ggtitle("Kelheim mobility with precipitation as line plot on same axis")

ggplotly(plt_ing_color)
# replace coluumn positioning

mob_joined_with_weather_berlin = mob_joined_with_weather_berlin %>% dplyr::select(year_week,prcp_week,tavg,snow_week,wspd,tmax,date,not_at_home_change)

mob_joined_with_weather_berlin = mob_joined_with_weather_berlin %>% mutate(landkreis = "Berlin")

mob_joined_with_ingolstadt = mob_joined_with_ingolstadt %>% mutate(landkreis = "Ingolstadt")

mob_joined_with_ing_berlin = rbind(mob_joined_with_ingolstadt,mob_joined_with_weather_berlin)
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = prcp_week,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,position =  position_dodge())

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = tavg,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,position = position_dodge2())

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = tmax,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2)

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = snow_week,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 50)

ggplotly(plt_hist_precip_ing)
Warning: Removed 7 rows containing non-finite values (stat_summary_bin).

After first look at data, we can assume that hours out of home strongly depend on average temperature outside, that sounds logical. Mb categorization seasons of the data will help to understand this function

mob_joined_with_ing_berlin = mob_joined_with_ing_berlin %>% 
  mutate(season = ifelse(month(date) %in% c(12,1,2),"winter",NA)) %>%
  mutate(season = ifelse(month(date) %in% c(3,4,5),"spring",season)) %>%
  mutate(season = ifelse(month(date) %in% c(6,7,8),"summer",season)) %>%
  mutate(season = ifelse(month(date) %in% c(9,10,11),"autumn",season))
#insert also a data about overall in germany
plt_hist_season = ggplot(mob_joined_with_ing_berlin,aes(x = season,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 5,position = position_dodge())

ggplotly(plt_hist_season)

So it seems that season has an enormous impact at mobility of citizens. Another important parameter can be description of the weather based on Kelheim statistics, we will merge it into Ingolstadt weather, because of the assumption, that Ingolstadt ad Kelheim have the similar weather properties.

type_of_weather = unique(weatherstack_kelheim$description)

weatherstack_kelheim_year_week = weatherstack_kelheim %>% mutate(year_week = paste0(isoyear(date),"-",isoweek(date)))

week_description_impact = weatherstack_kelheim_year_week %>% group_by(year_week) %>% count(description)

week_description_impact = week_description_impact %>% pivot_wider(names_from = description,values_from = n)

#remove NAs
week_description_impact[is.na(week_description_impact)] = 0
print(week_description_impact)
mob_joined_with_ingolstadt_description = mob_joined_with_ingolstadt %>% inner_join(week_description_impact, by = "year_week")
#normalize it to a percentage
#mob_joined_with_ingolstadt_description[type_of_weather] = mob_joined_with_ingolstadt_description[type_of_weather]/168 #168 hours a week

#Assumption not_at_home_change is calculated through individual weather impact normally
#=> each type of weather for the weak get its own weather impact based on not_at_home

#mob_joined_with_ingolstadt_description[type_of_weather] = mob_joined_with_ingolstadt_description[type_of_weather]*mob_joined_with_ingolstadt_description$not_at_home_change


print(mob_joined_with_ingolstadt_description)
mob_joined_with_ingolstadt_description_longer = mob_joined_with_ingolstadt_description%>% pivot_longer(cols = all_of(type_of_weather),names_to = "description",values_to = "value")# %>% filter(value!=0)

description_impact_overall = mob_joined_with_ingolstadt_description_longer %>% 
  filter(value!=0) %>% #mutate(value = value*not_at_home_change)%>% mutate(value = ifelse(value>0,value*not_at_home_change,-value*not_at_home_change)) %>%
  group_by(description) %>% summarize(impact = mean(not_at_home_change))

plot_ly(data = description_impact_overall,x = ~description,y = ~impact,type= "bar")

Another approach

map_vector <- c("Clear","Sunny","Cloudy","Light","Light","Light","Light","Light","Light","Light","Light","Medium","Cloudy","Light","Light","Heavy","Heavy","Heavy","Light","Medium","Heavy","Heavy","Light","Heavy","Heavy","Heavy","Heavy","Heavy","Heavy","Light","Medium","Medium","Light","Heavy","Light","Light","Light","Light","Light","Heavy","Light","Medium","Heavy","Heavy","Heavy")
names(map_vector)<- type_of_weather
mob_joined_with_ingolstadt_description_longer_mapped = mob_joined_with_ingolstadt_description_longer
mob_joined_with_ingolstadt_description_longer_mapped$description = map_vector[(mob_joined_with_ingolstadt_description_longer_mapped$description)]

description_impact_max = mob_joined_with_ingolstadt_description_longer_mapped %>% group_by(year_week)%>%
  top_n(1,value) %>% group_by(description) %>% summarize(impact = mean(not_at_home_change))

description_impact = mob_joined_with_ingolstadt_description_longer_mapped %>% group_by(year_week)%>%
  top_n(1,value)

week_calender = as.Date(seq(ISOdate(2014,1,3),ISOdate(2022,12,1),by="week"))
week_calender = data.frame(date = week_calender)
week_calender = week_calender %>% mutate(year_week = paste0(year(date),"-",isoweek(date)))

mob_joined_with_ingolstadt_description_longer_mapped = mob_joined_with_ingolstadt_description_longer_mapped %>% 
  inner_join(week_calender,by = "year_week")



plot_ly(data = description_impact_max,x = ~description,y = ~impact,type= "bar")

print(description_impact_max)
NA

Add stringency of covid policies to a data

json = fromJSON(file = "data/2022-10-08.json")
unlisted_json = unlist(json)
deu_stringency = unlisted_json[grep("DEU.stringency_actual",names(unlisted_json))]
date_stringency = sapply(strsplit(names(deu_stringency),split = ".",fixed = TRUE),"[[",2)
df_stringency = data.frame(date = date_stringency,stringency = deu_stringency)
df_stringency = df_stringency %>% mutate(year_week = paste0(year(date),"-",isoweek(date)),stringency = as.numeric(stringency))%>%ungroup() %>% group_by(year_week) %>% summarize(stringency = mean(stringency))

Insert new data from kexi region

demandDRT = read_delim("data/allDemandByDate.csv")
Rows: 528 Columns: 5── Column specification ─────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (4): noRides, noRequests, avgEuclidianDistance_m, avgTravelTime_s
date (1): date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Take a policy data into table

Statisctical evaluation of null hypothesis, independency tests

Starting with a model

After first data preparation and analysis, let’s try to make some predicitions about not_at_home duration based on plots that shown us a major impact on not_at_home variable, like tavg, season, description, tavg. Starting with a linear model and using Ingolstadt data limited by year 2020,2021

description_impact = description_impact %>% 
  mutate(season = ifelse(month(date) %in% c(12,1,2),"winter",NA)) %>%
  mutate(season = ifelse(month(date) %in% c(3,4,5),"spring",season)) %>%
  mutate(season = ifelse(month(date) %in% c(6,7,8),"summer",season)) %>%
  mutate(season = ifelse(month(date) %in% c(9,10,11),"autumn",season))
description_impact = description_impact %>% inner_join(df_stringency, by = "year_week")

Import holidays

holidays2020 = read_csv2("data/Holidays2020.csv") %>% dplyr::select(1,2,3)
holidays2021 = read_csv2("data/Holidays2021.csv") %>% dplyr::select(1,2,3)
holidays2022 = read_csv2("data/Holidays2022.csv") %>% dplyr::select(1,2,3)
holidays = rbind(holidays2020,holidays2021,holidays2022)
holidays = holidays %>% mutate(EndDateTime1 = as.Date(mdy_hm(EndDateTime1)),
                               StartDateTime1 = as.Date(mdy_hm(StartDateTime1)))

holiday_days = c(seq(holidays$StartDateTime1[1],holidays$EndDateTime1[1],by = "days"))

for(i in 1:nrow(holidays)){
  holiday_days = append(holiday_days,seq(holidays$StartDateTime1[i],holidays$EndDateTime1[i],by = "days"))
}

df_holidays = data.frame(date = holiday_days,isHoliday = TRUE)
demandDRT_week = demandDRT %>% mutate(year_week = paste0(year(date),"-",week(date)))%>% ungroup() %>% group_by(year_week) %>% summarize(noRides = sum(noRides),noRequests = sum(noRequests),avgEuclidianDistance_m = mean(avgEuclidianDistance_m), avgTravelTime_s = mean(avgTravelTime_s))
demand_table = demandDRT_week %>% inner_join(description_impact,by = "year_week")

best_pred <- demand_table %>% ungroup() %>%
  dplyr::select(-noRides,-landkreis,-description ,-year_week,-date,-value,-season,-noRequests,-avgEuclidianDistance_m,-not_at_home_change,-avgTravelTime_s) %>%
  map_dbl(cor,y = demand_table$noRides) %>%
  map_dbl(abs) %>%
  sort(decreasing = TRUE) 
print(best_pred)
      tmax       tavg stringency  snow_week       wspd  prcp_week 
 0.3344624  0.2882144  0.1875461  0.1854279  0.1403376  0.0963633 
best_pred <- description_impact %>% ungroup() %>%
  dplyr::select(-not_at_home_change,-landkreis,-description ,-year_week,-date,-value,-season) %>%
  map_dbl(cor,y = description_impact$not_at_home_change) %>%
  map_dbl(abs) %>%
  sort(decreasing = TRUE) 
print(best_pred)
stringency       tavg       tmax  snow_week  prcp_week       wspd 
0.68392232 0.19385347 0.11938358 0.08482803 0.06752787 0.05263554 


train_data = description_impact %>% left_join(df_holidays, by ="date") %>% replace_na(list(isHoliday = FALSE))


first_model = lm(not_at_home_change ~ ns(tavg,1)+date+stringency+description+isHoliday,
                 data = train_data)

mass_model = MASS::rlm(not_at_home_change ~ ns(tavg,2)+date+stringency+description,
                 data = train_data)

Need to check auto.arima approach

colors = c("actual" = "blue","predicted" = "red","residuals" = "gray50")
model = first_model
test_data = train_data %>% add_predictions(model = model) %>% add_residuals(model = model)

ggplotly(ggplot(test_data) +
  geom_line(aes(x = date,y = not_at_home_change,color = "actual"))+
  geom_line(aes(x = date,y = pred,color = "predicted"))+
  geom_line(aes(x = date,y = resid,color = "residuals"))+
  scale_color_manual(values = colors))
NA
barplot <- ggplot(test_data, aes(x = resid ))+
  geom_histogram(aes(y = stat(density)),colour="black", fill="white", binwidth=2)+
  geom_density( fill="#FF6666",adjust = 10,alpha = 0.5) 


ggplotly(barplot)
fitdistrplus::descdist(test_data$resid)
summary statistics
------
min:  -18.24719   max:  8.38169 
median:  0.3097064 
mean:  -2.333276e-14 
estimated sd:  4.700115 
estimated skewness:  -0.9682691 
estimated kurtosis:  4.967812 

normal_dist = fitdistrplus::fitdist(test_data$resid,"norm")
Warning: NaNs producedWarning: NaNs produced
plot(normal_dist)

x~y scatterplot, residual stderror, kexi verteilung

LS0tDQp0aXRsZTogIkJhZCB3ZWF0aGVyIEtlbGhlaW0gRGVtbyINCmF1dGhvcjogIk9sZWtzYW5kciBTb2JvbGlldiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogY29zbW8NCiAgICBoaWdobGlnaHQ6IG1vbm9jaHJvbWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCnJ1bnRpbWU6IHNoaW55DQplZGl0b3Jfb3B0aW9uczoNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyLCBpbmNsdWRlPSBGQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkocm1hcmtkb3duKQ0KbGlicmFyeShtb2RlbHIpDQpsaWJyYXJ5KHNwbGluZXMpDQpsaWJyYXJ5KGZvcmVjYXN0KQ0KbGlicmFyeShmaXRkaXN0cnBsdXMpDQpsaWJyYXJ5KHJqc29uKQ0KDQppZiAoImZpdGRpc3RycGx1cyIgJWluJSAoLnBhY2thZ2VzKCkpKXsNCiAgZGV0YWNoKHBhY2thZ2U6IGZpdGRpc3RycGx1cywgdW5sb2FkID0gVFJVRSkNCn0NCg0KaWYgKCJmaXRkaXN0cnBsdXMiICVpbiUgKC5wYWNrYWdlcygpKSl7DQogIGRldGFjaChwYWNrYWdlOiBNQVNTLCB1bmxvYWQgPSBUUlVFKQ0KfQ0KDQppZiAoImZpdGRpc3RycGx1cyIgJWluJSAoLnBhY2thZ2VzKCkpKXsNCiAgZGV0YWNoKHBhY2thZ2U6IHN0YXRzLCB1bmxvYWQgPSBUUlVFKQ0KfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyMgKipSZXNlYXJjaCBNZXRlb3N0YXQqKg0KDQpBZnRlciBzb21lIHJlc2VhcmNoZXMgYWJvdXQgbWV0ZW9zdGF0IGRhdGEgbmVhcmVzdCBzdGF0aW9uIGluIERFIHRoYXQgYmVsb25ncyB0byBLZWxoZWltIHJlZ2lvbiBhcmU6DQoNCi0gICAiTWFsbGVyc2RvcmYtUGZhZmZlbmJlcmcvTmllZGVyYmF5ZXJuIiB3aXRoIGFuIGlkOiAiRDMxNDciDQotICAgIk5ldW1hcmt0IC8gSMO2aGVuYmVyZyIgd2l0aCBhbmQgaWQ6ICI2OTExMCINCi0gICBVZWJ1bmdzZG9yZiAvIEVtaG9mDQoNCnRoZXJlIGFyZSBtYW55IG9mIHRoZW0gc28gSSBhbSBzdGFydGluZyB0byB0aGluayBhYm91dCBleHRyYWN0aW5nIGFsbCBmcm9tIHRoZSBCYXllcm4gb3IgZXh0cmFjdCB0aGUgbmVhcmVzdCBmcm9tIGxvbmd0aXR1ZGUvbGF0aXR1ZGUgcG9pbnQgd2l0aCB0aGUgS2VsaGVpbSBzaGFwZWZpbGUodXNpbmcganNvbiBhbmQgRXVjbGlkIGRpc3RhbmNlcykNCg0KW0tlbGhlaW0gaGFzIG5vIHdlYXRoZXIgc3RhdGlvbiwgYnV0IGl0IGNvdWxkIGJlIHJlY29uc3RydWN0ZWQgd2l0aCAyIG90aGVyXShodHRwczovL3dlYXRoZXJzcGFyay5jb20veS83MDM3MC9BdmVyYWdlLVdlYXRoZXItaW4tS2VsaGVpbS1HZXJtYW55LVllYXItUm91bmQpDQoNCkhvaGVuZmVscyB3aXRoIGlkOiAiMTA3NzUiIGFuZCBJbmdvbHN0YWR0IHdpdGggaWQ6IjEwODYwIiAqKmtlbGhlaW1fZGF0YSA9IHt3ZWlnaHQxfXh7aG9oZW5mZWxzfSArIHt3ZWlnaHQyfXh7aW5nbHN0YWR0fSoqDQoNCkFsc28gdGhpcyBzaXRlIHNob3dzLCB0aGF0IHRoZXJlIGFyZSBtYW55IG9mIHRoZSBLZWxoZWltIHN0YXRpb25zIGluIHRoaXMgYXJlYSwgYnV0IG1ldGVvc3RhdCBkb2Vzbid0IGNvbnRhaW4gdGhlbSA8aHR0cHM6Ly93d3cud3VuZGVyZ3JvdW5kLmNvbS9kYXNoYm9hcmQvcHdzL0lLRUxIRTU+DQoNCiMjICoqUmVzZWFyY2ggV2VhdGhlcnN0YWNrKioNCg0KYGBge3IgZmlyc3QgbG9vayBhdCB3ZWF0aGVyc3RhY2sgZGF0YSBzcGVjaWZpYyB0byBLZWxoZWltfQ0Kd2VhdGhlcnN0YWNrX2tlbGhlaW0gPSByZWFkX2RlbGltKCJkYXRhL0tlbGhlaW1fd2VhdGhlcl9zaW5jZV9qdWx5XzIwMDguY3N2IixkZWxpbSA9ICIsIikNCnByaW50KHdlYXRoZXJzdGFja19rZWxoZWltKQ0KYGBgDQoNCldoYXQgdG8gdGFrZSBhcyBhIHJlZmZlciBwb2ludCBpc24ndCBjbGVhciBiZWNhdXNlIG9mIHRoZSBkYXRlKGJlZm9yZS9hZnRlciBjb3ZpZCkgYW5kIHdlYXRoZXIgdHlwZSAoc3VubnksY2xlYXIsdGVtcGVyYXR1cmUpIEFsc28gdGhlcmUgaXMgbm8gdGVtcGVyYXR1cmUgaW4gaXQgOi8NCg0KIyMgKipJbXBvcnQgbW9iaWxpdHkgZnJvbSBHb29nbGUqKg0KDQpgYGB7ciBpbmNsdWRpbmcgZ29vZ2xlIGdlcm1hbnkgbW9iaWxpdHkgZGF0YSxtZXNzYWdlPUZBTFNFfQ0KI2dsb2JhbF9tb2JpbGl0eSA9IHJlYWRfZGVsaW0oImh0dHBzOi8vd3d3LmdzdGF0aWMuY29tL2NvdmlkMTkvbW9iaWxpdHkvR2xvYmFsX01vYmlsaXR5X1JlcG9ydC5jc3YiLCIsIikNCiNkZV9tb2JpbGl0eSA9IGdsb2JhbF9tb2JpbGl0eSAlPiUgZmlsdGVyKGNvdW50cnlfcmVnaW9uX2NvZGUgPT0gIkRFIikNCmBgYA0KDQpgYGB7ciB3aGF0IHJlZ2lvbnMgYXJlIGRhdGEgcHJvdmlkZWR9DQojcHJpbnQodW5pcXVlKGRlX21vYmlsaXR5JHN1Yl9yZWdpb25fMSkpDQoNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlIHRoZSBtb3N0IHByZWNpc2UgcmVnaW9uIHRvIGZpbHRlciBkYXRhIGZyb20gaXMgQmF2YXJpYSA6Lw0KDQpSZWxldmFudCBkYXRhIGZvciB0aGUgLCBtb2JpbGl0eQ0KDQpgYGB7ciBtb2JpbGl0eSBkYXRhIGJhdmFyaWF9DQojYmF2YXJpYV9tb2JpbGl0eSA9IGRlX21vYmlsaXR5ICU+JSBmaWx0ZXIoc3ViX3JlZ2lvbl8xID09ICJCYXZhcmlhIikNCiNiYXZhcmlhX21vYmlsaXR5ID0gYmF2YXJpYV9tb2JpbGl0eSAlPiUgI2RwbHlyOjpzZWxlY3QoY291bnRyeV9yZWdpb24sc3ViX3JlZ2lvbl8xLGRhdGUscmVzaWRlbnRpYWxfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSkgJT4lDQojICBtdXRhdGUocmVzaWRlbnRpYWxfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSA9IC1yZXNpZGVudGlhbF9wZXJjZW50X2NoYW5nZV9mcm9tX2Jhc2VsaW5lLA0KIyAgICAgICAgIHNvdXJjZSA9ICJHb29nbGUiKSU+JQ0KIyAgcmVuYW1lKEJ1bmRlc2xhbmRJRCA9IHN1Yl9yZWdpb25fMSxub3RfYXRfaG9tZV9jaGFuZ2UgPSByZXNpZGVudGlhbF9wZXJjZW50X2NoYW5nZV9mcm9tX2Jhc2VsaW5lKQ0KI2JhdmFyaWFfbW9iaWxpdHkgPSBiYXZhcmlhX21vYmlsaXR5ICU+JSBkcGx5cjo6c2VsZWN0KGRhdGUsQnVuZGVzbGFuZElELG5vdF9hdF9ob21lX2NoYW5nZSxzb3VyY2UpDQojTmVlZCB0byBmaWx0ZXIgb3V0IHdlZWtlbmRzDQoNCiNwbHQgPSBnZ3Bsb3QoYmF2YXJpYV9tb2JpbGl0eSkrDQojICBnZW9tX3BvaW50KGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlKSkNCiNnZ3Bsb3RseShwbHQpDQpgYGANCg0KIyMgKipJbXBvcnQgbW9iaWxpdHkgZnJvbSBTZW5vem9uKioNCg0KYGBge3IgaW1wb3J0IGZyb20gc2Vub3pvbn0NCnNuel9tb2JpbGl0eSA9IHJlYWRfZGVsaW0oImRhdGEvTEtfbW9iaWxpdHlEYXRhX3dlZWtkYXlzLmNzdiIsIjsiKQ0KDQojS2VsaGVpbQ0Kc256X21vYmlsaXR5X2tlbGhlaW0gPSBzbnpfbW9iaWxpdHkgJT4lIGZpbHRlcihMYW5ka3JlaXMgPT0gIkxhbmRrcmVpcyBLZWxoZWltIikgJT4lIG11dGF0ZShzb3VyY2UgPSAic2Vub3pvbiIpICU+JSBkcGx5cjo6c2VsZWN0KC1vdXRPZkhvbWVEdXJhdGlvbikgJT4lIHJlbmFtZShub3RfYXRfaG9tZV9jaGFuZ2UgPSBwZXJjZW50YWdlQ2hhbmdlQ29tcGFyZWRUb0JlZm9yZUNvcm9uYSkNCnNuel9tb2JpbGl0eV9rZWxoZWltJGRhdGUgPSBhcy5EYXRlKHN0cnB0aW1lKHNuel9tb2JpbGl0eV9rZWxoZWltJGRhdGUsIiVZJW0lZCIpKQ0KDQojQmVybGluDQpzbnpfbW9iaWxpdHlfYmVybGluID0gc256X21vYmlsaXR5ICU+JSBmaWx0ZXIoTGFuZGtyZWlzID09ICJCZXJsaW4iKSAlPiUgbXV0YXRlKHNvdXJjZSA9ICJzZW5vem9uIikgJT4lIGRwbHlyOjpzZWxlY3QoLW91dE9mSG9tZUR1cmF0aW9uKSAlPiUgcmVuYW1lKG5vdF9hdF9ob21lX2NoYW5nZSA9IHBlcmNlbnRhZ2VDaGFuZ2VDb21wYXJlZFRvQmVmb3JlQ29yb25hKQ0Kc256X21vYmlsaXR5X2JlcmxpbiRkYXRlID0gYXMuRGF0ZShzdHJwdGltZShzbnpfbW9iaWxpdHlfYmVybGluJGRhdGUsIiVZJW0lZCIpKQ0KDQpjb2xvcnMgPC0gYygiQmVybGluIiA9ICJibHVlIiwgIktlbGhlaW0iID0gInJlZCIpDQoNCnBsdCA9IGdncGxvdCgpKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzbnpfbW9iaWxpdHlfa2VsaGVpbSxhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxjb2xvciA9ICJCZXJsaW4iKSkrDQogIGdlb21fcG9pbnQoZGF0YSA9IHNuel9tb2JpbGl0eV9iZXJsaW4sYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsY29sb3IgPSAiS2VsaGVpbSIpKSsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xvcnMpDQpnZ3Bsb3RseShwbHQpDQpgYGANCg0KIyMgKipBZ2dyZWdhdGUgMiBzb3VyY2VzKioNCg0KV2UgdGFrZSBiZXJsaW4gd2VhdGhlciBmcm9tIHN0YXRpb24gaW4gU2Nob2VuZWZlbGQgd2l0aCBpZCA9IDEwMzg0DQpgYGB7ciBhZGRpbmcgYmVybGluIHdlYXRoZXIsIGVjaG89RkFMU0V9DQpiZXJsaW5fd2VhdGhlcl9kYWlseSA9IHJlYWRfZGVsaW0oImh0dHBzOi8vYnVsay5tZXRlb3N0YXQubmV0L3YyL2RhaWx5LzEwMzg1LmNzdi5neiIsIiwiLGNvbF9uYW1lcyA9IEZBTFNFKQ0KY29sbmFtZXMoYmVybGluX3dlYXRoZXJfZGFpbHkpID0gYygiZGF0ZSIsICJ0YXZnIiwgInRtaW4iLCAidG1heCIsICJwcmNwIiwgInNub3ciLCAid2RpciIsICJ3c3BkIiwgIndwZ3QiLCAicHJlcyIsICJ0c3VuIikNCmBgYA0KDQoNCmBgYHtyIGRlZmluZSB3ZWF0aGVyIGRhdGEgYXMgd2VlayBkYXRhfQ0KIyB0aGluayBhYm91dCBkdXJhdGlvbiBvZiBkZXNjcmlwdGlvbiBjb2x1bW4NCiMgS2VsaGVpbQ0Kd2VhdGhlcnN0YWNrX2tlbGhlaW1fZGFpbHkgPSB3ZWF0aGVyc3RhY2tfa2VsaGVpbSAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lDQogIHN1bW1hcml6ZShkZXNjcmlwdGlvbiA9IGRlc2NyaXB0aW9uLHByZWNpcF9kYXkgPSBzdW0ocHJlY2lwKSx2aXNpYmlsaXR5X21lYW4gPSBtZWFuKHZpc2liaWxpdHkpLHRvdGFsc25vd19kYWlseSA9IG1lYW4odG90YWxzbm93X2RhaWx5KSkNCiAgDQp3ZWF0aGVyc3RhY2tfa2VsaGVpbV93ZWVrbHkgPSB3ZWF0aGVyc3RhY2tfa2VsaGVpbV9kYWlseSAlPiUgDQogIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoaXNveWVhcihkYXRlKSwiLSIsaXNvd2VlayhkYXRlKSkpICU+JQ0KICBncm91cF9ieSh5ZWFyX3dlZWspICU+JQ0KICBzdW1tYXJpemUoZGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbixkYXRlID0gZmlyc3QoZGF0ZSksIHByZWNpcF93ZWVrID0gc3VtKHByZWNpcF9kYXkpLHZpc2liaWxpdHlfbWVhbiA9IG1lYW4odmlzaWJpbGl0eV9tZWFuKSx0b3RhbHNub3dfd2Vla2x5ID1zdW0oIHRvdGFsc25vd19kYWlseSkpDQp3ZWF0aGVyc3RhY2tfa2VsaGVpbV93ZWVrbHkgPSB1bmlxdWUod2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5KQ0KcHJpbnQod2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5KQ0KDQojQmVybGluDQogIA0KYmVybGluX3dlYXRoZXJfd2Vla2x5ID0gYmVybGluX3dlYXRoZXJfZGFpbHkgJT4lIGZpbHRlcih5ZWFyKGRhdGUpID49MjAyMCkgJT4lDQogIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoaXNveWVhcihkYXRlKSwiLSIsaXNvd2VlayhkYXRlKSkpICU+JQ0KICBncm91cF9ieSh5ZWFyX3dlZWspICU+JQ0KICBzdW1tYXJpemUoZGF0ZSA9IGZpcnN0KGRhdGUpLCBwcmNwX3dlZWsgPSBzdW0ocHJjcCksIHRhdmc9IG1lYW4odGF2Zyksc25vd193ZWVrID1zdW0oIHNub3cpLHdzcGQgPSBtZWFuKHdzcGQpLHRtYXggPSBtYXgodG1heCkpICU+JQ0KICBhcnJhbmdlKHllYXJfd2VlaykNCnByaW50KGJlcmxpbl93ZWF0aGVyX3dlZWtseSkNCiAgDQpgYGANCg0KYGBge3IgZ29vZ2xlK3Nlbm96b24rd2VhdGhlcn0NCiNtb2Jfam9pbmVkID0gcmJpbmQoc256X21vYmlsaXR5X2tlbGhlaW0sYmF2YXJpYV9tb2JpbGl0eSkNCiNLZWxoZWltDQpzbnpfbW9iaWxpdHlfa2VsaGVpbV95ZWFyX3dlZWsgPSBzbnpfbW9iaWxpdHlfa2VsaGVpbSAlPiUgDQogIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoaXNveWVhcihkYXRlKSwiLSIsaXNvd2VlayhkYXRlKSkpICU+JQ0KICBncm91cF9ieSh5ZWFyX3dlZWspICU+JQ0KICBzdW1tYXJpemUoZGF0ZSA9IGZpcnN0KGRhdGUpLG5vdF9hdF9ob21lX2NoYW5nZSA9IG1lYW4obm90X2F0X2hvbWVfY2hhbmdlKSkNCm1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2tlbGhlaW0gPSBzbnpfbW9iaWxpdHlfa2VsaGVpbV95ZWFyX3dlZWsgJT4lIGlubmVyX2pvaW4od2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5LCBieSA9ICJ5ZWFyX3dlZWsiKSAlPiUgZHBseXI6OnNlbGVjdCgtZGF0ZS55KSAlPiUgcmVuYW1lKGRhdGUgPSBkYXRlLngpDQpwcmludChtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9rZWxoZWltKQ0KDQojQmVybGluDQpzbnpfbW9iaWxpdHlfYmVybGluX3llYXJfd2VlayA9IHNuel9tb2JpbGl0eV9iZXJsaW4gJT4lIA0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRhdGUgPSBmaXJzdChkYXRlKSxub3RfYXRfaG9tZV9jaGFuZ2UgPSBtZWFuKG5vdF9hdF9ob21lX2NoYW5nZSkpDQptb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gPSBzbnpfbW9iaWxpdHlfYmVybGluX3llYXJfd2VlayAlPiUgaW5uZXJfam9pbihiZXJsaW5fd2VhdGhlcl93ZWVrbHksIGJ5ID0gInllYXJfd2VlayIpICU+JSBkcGx5cjo6c2VsZWN0KC1kYXRlLnkpICU+JSByZW5hbWUoZGF0ZSA9IGRhdGUueCkNCnByaW50KG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2JlcmxpbikNCmBgYA0KDQpgYGB7ciBmaXJzdCBwbG90fQ0KI0ZpcnN0IHBsb3Qgd2l0aCBjb2xvdXIgYXMgcHJlY2lwaXRhdGlvbg0Kc2hhcGVzIDwtIGMoIkJlcmxpbiIgPSA1LCAiS2VsaGVpbSIgPSAzKQ0KcGx0X2NvbG9yID0gZ2dwbG90KCkrDQogIGdlb21fcG9pbnQoZGF0YSA9IG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2tlbGhlaW0sYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsY29sb3VyID0gcHJlY2lwX3dlZWssc2hhcGUgPSAiS2VsaGVpbSIpKSsNCiAgI2dlb21fcG9pbnQoZGF0YSA9IG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2JlcmxpbixhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxjb2xvdXIgPSBwcmNwX3dlZWssc2hhcGUgPSAiQmVybGluIikpKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudDIoKSsNCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IHNoYXBlcykNCiAgDQoNCmdncGxvdGx5KHBsdF9jb2xvcikNCmBgYA0KDQpgYGB7ciBzZWNvbmQgcGxvdH0NCiNTZWNvbmQgcGxvdCBhcyBhbm90aGVyIGxpbmUgYXMgcHJlY2lwaXRhdGlvbg0KcGx0X2xpbmUgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSkrDQogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UpKSsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0ZSx5ID0gcHJlY2lwX3dlZWsqMC41LGNvbG9yID0gInJlZCIpKQ0KICANCg0KZ2dwbG90bHkocGx0X2xpbmUpDQpgYGANCg0KYGBge3IgcHJlY2lwaXRhdGlvbiBoaXN0b2dyYW19DQpwbHRfaGlzdF9wcmVjaXAgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSxhZXMoeCA9IHByZWNpcF93ZWVrLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gMixmaWxsID0gImJsdWUiKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF9wcmVjaXApDQpgYGANCg0KYGBge3IgdmlzaWJpbGl0eSBoaXN0b2dyYW19DQpwbHRfaGlzdF92aXNpYmlsaXR5ID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2tlbGhlaW0sYWVzKHggPSB2aXNpYmlsaXR5X21lYW4seSA9IG5vdF9hdF9ob21lX2NoYW5nZSkpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAwLjUsZmlsbCA9ICJibHVlIikNCg0KZ2dwbG90bHkocGx0X2hpc3RfdmlzaWJpbGl0eSkNCmBgYA0KDQpgYGB7ciB0b3RhbHNub3cgaGlzdG9ncmFtfQ0KcGx0X2hpc3RfdG90YWxzbm93ID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2tlbGhlaW0sYWVzKHggPSB0b3RhbHNub3dfd2Vla2x5LHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNyxmaWxsID0gImJsdWUiKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF90b3RhbHNub3cpDQpgYGANCg0KYGBge3IgbG9va2luZyBhdCBkZXNjcmlwdGlvbiBjb2x1bW59DQojdGhpcyBpcyBhIGJhZCBwbG90IGJlY2F1c2UgaXQgdGFrZXMgZGVzY3JpcHRpb24gb2YgMSBkYXkgb2YgdGhlIHdlZWsNCnBsdF9oaXN0X2Rlc2NyID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2tlbGhlaW0sYWVzKHggPSBkZXNjcmlwdGlvbix5ID0gbm90X2F0X2hvbWVfY2hhbmdlKSkrDQogIHN0YXRfc3VtbWFyeV9iaW4oZnVuID0gIm1lYW4iLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAiYmFyIiwNCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDUsZmlsbCA9ICJibHVlIikrDQogIGNvb3JkX2ZsaXAoKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF9kZXNjcikNCmBgYA0KDQojIyBMZXQncyB0cnkgaXQgb3V0IHdpdGggbWV0ZW9zdGF0IGRhdGEsIHRoYXQgY29udGFpbnMgc2NvcGUgb2YgdGhlIDIwMjIgd2l0aG91dCByZXN0aWN0aW9ucw0KDQpJbmdvbHN0YWR0IGRhdGEgZnJvbSBpZCA9IDEwODYwIHN0YXRpb24NCg0KYGBge3IgSW5nb2xzdGFkdCBkYXRhfQ0KaW5nb2xzdGFkdF93ZWF0aGVyID0gcmVhZF9kZWxpbSgiaHR0cHM6Ly9idWxrLm1ldGVvc3RhdC5uZXQvdjIvZGFpbHkvMTA4NjAuY3N2Lmd6IiwiLCIsY29sX25hbWVzID0gRkFMU0UpDQoNCmNvbG5hbWVzKGluZ29sc3RhZHRfd2VhdGhlcikgPSBjKCJkYXRlIiwgInRhdmciLCAidG1pbiIsICJ0bWF4IiwgInByY3AiLCAic25vdyIsICJ3ZGlyIiwgIndzcGQiLCAid3BndCIsICJwcmVzIiwgInRzdW4iKQ0KDQoNCiMgV2UgZG9uJ3QgbmVlZCBkYXRhIG9mIHdlYXRoZXIgYmVmb3JlIDIwMjAsIGJlY2F1c2Ugb2Ygc256X21vYmlsaXR5IGRhdGUsIGFsc28gZGF0YSBpc24ndCBwcmVjaXNlDQoNCmluZ29sc3RhZHRfd2VhdGhlciA9IGluZ29sc3RhZHRfd2VhdGhlciAlPiUgZmlsdGVyKHllYXIoZGF0ZSk+PTIwMjApJT4lIHJlcGxhY2VfbmEobGlzdChzbm93ID0gMCkpDQoNCnByaW50KGluZ29sc3RhZHRfd2VhdGhlcikNCmBgYA0KDQpIb2hlbmZlbHMgZGF0YSBmcm9tIGlkID0gMTA3NzUgc3RhdGlvbg0KDQpgYGB7ciBIb2hlbmZlbHMgZGF0YX0NCmhvaGVuZmVsc193ZWF0aGVyID0gcmVhZF9kZWxpbSgiaHR0cHM6Ly9idWxrLm1ldGVvc3RhdC5uZXQvdjIvZGFpbHkvMTA3NzUuY3N2Lmd6IiwiLCIsY29sX25hbWVzID0gRkFMU0UpDQoNCmNvbG5hbWVzKGhvaGVuZmVsc193ZWF0aGVyKSA9IGMoImRhdGUiLCAidGF2ZyIsICJ0bWluIiwgInRtYXgiLCAicHJjcCIsICJzbm93IiwgIndkaXIiLCAid3NwZCIsICJ3cGd0IiwgInByZXMiLCAidHN1biIpDQoNCg0KIyBXZSBkb24ndCBuZWVkIGRhdGEgb2Ygd2VhdGhlciBiZWZvcmUgMjAyMCwgYmVjYXVzZSBvZiBzbnpfbW9iaWxpdHkgZGF0ZSwgYWxzbyBkYXRhIGlzbid0IHByZWNpc2UNCg0KaG9oZW5mZWxzX3dlYXRoZXIgPSBob2hlbmZlbHNfd2VhdGhlciAlPiUgZmlsdGVyKHllYXIoZGF0ZSk+PTIwMjApICU+JSByZXBsYWNlX25hKGxpc3Qoc25vdyA9IDApKQ0KDQpwcmludChob2hlbmZlbHNfd2VhdGhlcikNCmBgYA0KDQpBcyB3ZSBjYW4gc2VlIGluIEhvaGVuZmVscyBkYXRhIGlzbid0IHRoYXQgYWNjdXJhdGUgYW5kIHByZWNpcGl0YXRpb24gaXMgZGF0YSBpcyBtaXNzaW5nIGZyIHllYXIgMjAyMCwgc28gZm9yIHRoZSBmdXJ0aGVyIGFuYWx5c2lzIHdlIHRha2Ugb25seSBJbmdvbHN0YWR0IGRhdGEuDQoNCmBgYHtyIEluZ29sc3RhZHQgd2Vla2x5IGRhdGF9DQppbmdvbHN0YWR0X3dlYXRoZXJfd2Vla2x5ID0gaW5nb2xzdGFkdF93ZWF0aGVyICU+JSANCiAgbXV0YXRlKHllYXJfd2VlayA9IHBhc3RlMChpc295ZWFyKGRhdGUpLCItIixpc293ZWVrKGRhdGUpKSkgJT4lDQogIGdyb3VwX2J5KHllYXJfd2VlaykgJT4lDQogIHN1bW1hcml6ZShkYXRlID0gZmlyc3QoZGF0ZSksIHByY3Bfd2VlayA9IHN1bShwcmNwKSwgdGF2Zz0gbWVhbih0YXZnKSxzbm93X3dlZWsgPXN1bSggc25vdyksd3NwZCA9IG1lYW4od3NwZCksdG1heCA9IG1heCh0bWF4KSkgJT4lDQogIGFycmFuZ2UoeWVhcl93ZWVrKQ0KI2luZ29sc3RhZHRfd2VhdGhlcl93ZWVrbHkgPSB1bmlxdWUod2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5KQ0KcHJpbnQoaW5nb2xzdGFkdF93ZWF0aGVyX3dlZWtseSkNCmBgYA0KDQpgYGB7ciBhZ2dyZWdhdGUgc256IGFuZCBpbmdvbHN0YWR0fQ0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQgPSBpbmdvbHN0YWR0X3dlYXRoZXJfd2Vla2x5ICU+JSANCiAgaW5uZXJfam9pbihzbnpfbW9iaWxpdHlfa2VsaGVpbV95ZWFyX3dlZWssIGJ5ID0gInllYXJfd2VlayIpICU+JSANCiAgZHBseXI6OnNlbGVjdCgtZGF0ZS54KSAlPiUNCiAgcmVuYW1lKGRhdGUgPSBkYXRlLnkpICU+JQ0KICByZXBsYWNlX25hKGxpc3QodG1heCA9IDApKQ0KDQpwcmludChtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCkNCmBgYA0KDQpgYGB7ciBjb2xvcmVkIHByZWNpcGl0YXRpb24gcGxvdCBJbmdvbHN0YWR0fQ0KI0ZpcnN0IHBsb3Qgd2l0aCBjb2xvdXIgYXMgcHJlY2lwaXRhdGlvbg0KZmlsbHMgPC0gYygiSW5nb2xzdGFkdCIgPSAiYmx1ZSIsICJCZXJsaW4iID0gInJlZCIpDQpwbHRfaW5nX2NvbG9yID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0KSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxjb2xvdXIgPSBwcmNwX3dlZWssZmlsbCA9ICJJbmdvbHN0YWR0IikpKw0KICBnZW9tX3BvaW50KGRhdGEgPSBtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4sYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsY29sb3VyID0gcHJjcF93ZWVrLGZpbGwgPSAiQmVybGluIikpKw0KICBzY2FsZV9jb2xvcl9ncmFkaWVudChsb3cgPSAid2hpdGUiLGhpZ2ggPSAiYmxhY2siKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZmlsbHMpDQoNCmdncGxvdGx5KHBsdF9pbmdfY29sb3IpDQpgYGANCg0KDQpgYGB7ciBwcmVjaXBpdGF0aW9uIGFzIGxpbmUgcGxvdH0NCnBsdF9pbmdfY29sb3IgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQpKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlKSkrDQogIGdlb21fbGluZShhZXMoeCA9IGRhdGUseSA9IHByY3Bfd2Vlayxjb2xvciA9ICJJbmdvbHN0YWR0IikpKw0KICAjZ2VvbV9saW5lKGRhdGEgPSBiZXJsaW5fd2VhdGhlcl93ZWVrbHksYWVzKHggPSBkYXRlLHkgPSBwcmNwX3dlZWssY29sb3IgPSAiQmVybGluIikpKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZmlsbHMpKw0KICBnZ3RpdGxlKCJLZWxoZWltIG1vYmlsaXR5IHdpdGggcHJlY2lwaXRhdGlvbiBhcyBsaW5lIHBsb3Qgb24gc2FtZSBheGlzIikNCg0KZ2dwbG90bHkocGx0X2luZ19jb2xvcikNCmBgYA0KDQpgYGB7ciBqb2luIGluZ29sc3RhZHQgYW5kIGJlcmxpbiBpbiAxIHRhYmxlfQ0KIyByZXBsYWNlIGNvbHV1bW4gcG9zaXRpb25pbmcNCg0KbW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfYmVybGluID0gbW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfYmVybGluICU+JSBkcGx5cjo6c2VsZWN0KHllYXJfd2VlayxwcmNwX3dlZWssdGF2Zyxzbm93X3dlZWssd3NwZCx0bWF4LGRhdGUsbm90X2F0X2hvbWVfY2hhbmdlKQ0KDQptb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gPSBtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gJT4lIG11dGF0ZShsYW5ka3JlaXMgPSAiQmVybGluIikNCg0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCAlPiUgbXV0YXRlKGxhbmRrcmVpcyA9ICJJbmdvbHN0YWR0IikNCg0KbW9iX2pvaW5lZF93aXRoX2luZ19iZXJsaW4gPSByYmluZChtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCxtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4pDQpgYGANCg0KYGBge3IgaGlzdG9ncmFtIHdpdGggcHJlY2lwfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHByY3Bfd2Vlayx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGZpbGwgPSBsYW5ka3JlaXMpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gMixwb3NpdGlvbiA9ICBwb3NpdGlvbl9kb2RnZSgpKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF9wcmVjaXBfaW5nKQ0KDQpgYGANCg0KYGBge3IgaGlzdG9ncmFtIHdpdGggYXZlcmFnZSB0ZW1wZXJhdHVyZX0NCnBsdF9oaXN0X3ByZWNpcF9pbmcgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX2luZ19iZXJsaW4sYWVzKHggPSB0YXZnLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsZmlsbCA9IGxhbmRrcmVpcykpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAyLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKCkpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcF9pbmcpDQoNCmBgYA0KDQpgYGB7ciBoaXN0b2dyYW0gd2l0aCBtYXhpbWFsIHRlbXBlcmF0dXJlfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHRtYXgseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxmaWxsID0gbGFuZGtyZWlzKSkrDQogIHN0YXRfc3VtbWFyeV9iaW4oZnVuID0gIm1lYW4iLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAiYmFyIiwNCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDIpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcF9pbmcpDQoNCmBgYA0KDQpgYGB7ciBoaXN0b2dyYW0gd2l0aCBzbm93IGF0IHRoZSB3ZWVrfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHNub3dfd2Vlayx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGZpbGwgPSBsYW5ka3JlaXMpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNTApDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcF9pbmcpDQoNCmBgYA0KDQpBZnRlciBmaXJzdCBsb29rIGF0IGRhdGEsIHdlIGNhbiBhc3N1bWUgdGhhdCBob3VycyBvdXQgb2YgaG9tZSBzdHJvbmdseSBkZXBlbmQgb24gYXZlcmFnZSB0ZW1wZXJhdHVyZSBvdXRzaWRlLCB0aGF0IHNvdW5kcyBsb2dpY2FsLiBNYiBjYXRlZ29yaXphdGlvbiBzZWFzb25zIG9mIHRoZSBkYXRhIHdpbGwgaGVscCB0byB1bmRlcnN0YW5kIHRoaXMgZnVuY3Rpb24NCg0KYGBge3IgYWRkaW5nIG5ldyBzZWFzb24gY29sdW1ufQ0KbW9iX2pvaW5lZF93aXRoX2luZ19iZXJsaW4gPSBtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbiAlPiUgDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDEyLDEsMiksIndpbnRlciIsTkEpKSAlPiUNCiAgbXV0YXRlKHNlYXNvbiA9IGlmZWxzZShtb250aChkYXRlKSAlaW4lIGMoMyw0LDUpLCJzcHJpbmciLHNlYXNvbikpICU+JQ0KICBtdXRhdGUoc2Vhc29uID0gaWZlbHNlKG1vbnRoKGRhdGUpICVpbiUgYyg2LDcsOCksInN1bW1lciIsc2Vhc29uKSkgJT4lDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDksMTAsMTEpLCJhdXR1bW4iLHNlYXNvbikpDQpgYGANCg0KDQpgYGB7ciBzZWFzb24gfiBub3RfYXRfaG9tZTogIHBsb3RzfQ0KI2luc2VydCBhbHNvIGEgZGF0YSBhYm91dCBvdmVyYWxsIGluIGdlcm1hbnkNCnBsdF9oaXN0X3NlYXNvbiA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHNlYXNvbix5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGZpbGwgPSBsYW5ka3JlaXMpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNSxwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCkpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3NlYXNvbikNCmBgYA0KDQoNClNvIGl0IHNlZW1zIHRoYXQgc2Vhc29uIGhhcyBhbiBlbm9ybW91cyBpbXBhY3QgYXQgbW9iaWxpdHkgb2YgY2l0aXplbnMuDQpBbm90aGVyIGltcG9ydGFudCBwYXJhbWV0ZXIgY2FuIGJlIGRlc2NyaXB0aW9uIG9mIHRoZSB3ZWF0aGVyIGJhc2VkIG9uIEtlbGhlaW0gc3RhdGlzdGljcywgd2Ugd2lsbCBtZXJnZSBpdCBpbnRvIEluZ29sc3RhZHQgd2VhdGhlciwgYmVjYXVzZSBvZiB0aGUgYXNzdW1wdGlvbiwgdGhhdCBJbmdvbHN0YWR0IGFkIEtlbGhlaW0gaGF2ZSB0aGUgc2ltaWxhciB3ZWF0aGVyIHByb3BlcnRpZXMuDQoNCmBgYHtyIGFkZGluZyBkZXNjcmlwdGlvbiB0byBhbiBpbmdvbHN0YWR0IGRhdGF9DQp0eXBlX29mX3dlYXRoZXIgPSB1bmlxdWUod2VhdGhlcnN0YWNrX2tlbGhlaW0kZGVzY3JpcHRpb24pDQoNCndlYXRoZXJzdGFja19rZWxoZWltX3llYXJfd2VlayA9IHdlYXRoZXJzdGFja19rZWxoZWltICU+JSBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKQ0KDQp3ZWVrX2Rlc2NyaXB0aW9uX2ltcGFjdCA9IHdlYXRoZXJzdGFja19rZWxoZWltX3llYXJfd2VlayAlPiUgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUgY291bnQoZGVzY3JpcHRpb24pDQoNCndlZWtfZGVzY3JpcHRpb25faW1wYWN0ID0gd2Vla19kZXNjcmlwdGlvbl9pbXBhY3QgJT4lIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBkZXNjcmlwdGlvbix2YWx1ZXNfZnJvbSA9IG4pDQoNCiNyZW1vdmUgTkFzDQp3ZWVrX2Rlc2NyaXB0aW9uX2ltcGFjdFtpcy5uYSh3ZWVrX2Rlc2NyaXB0aW9uX2ltcGFjdCldID0gMA0KcHJpbnQod2Vla19kZXNjcmlwdGlvbl9pbXBhY3QpDQpgYGANCmBgYHtyIGpvaW4gaXQgd2l0aCBtb2JpbGl0eSBkYXRhIEluZ29sc3RhZHR9DQptb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbiA9IG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0ICU+JSBpbm5lcl9qb2luKHdlZWtfZGVzY3JpcHRpb25faW1wYWN0LCBieSA9ICJ5ZWFyX3dlZWsiKQ0KI25vcm1hbGl6ZSBpdCB0byBhIHBlcmNlbnRhZ2UNCiNtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvblt0eXBlX29mX3dlYXRoZXJdID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25bdHlwZV9vZl93ZWF0aGVyXS8xNjggIzE2OCBob3VycyBhIHdlZWsNCg0KI0Fzc3VtcHRpb24gbm90X2F0X2hvbWVfY2hhbmdlIGlzIGNhbGN1bGF0ZWQgdGhyb3VnaCBpbmRpdmlkdWFsIHdlYXRoZXIgaW1wYWN0IG5vcm1hbGx5DQojPT4gZWFjaCB0eXBlIG9mIHdlYXRoZXIgZm9yIHRoZSB3ZWFrIGdldCBpdHMgb3duIHdlYXRoZXIgaW1wYWN0IGJhc2VkIG9uIG5vdF9hdF9ob21lDQoNCiNtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvblt0eXBlX29mX3dlYXRoZXJdID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25bdHlwZV9vZl93ZWF0aGVyXSptb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbiRub3RfYXRfaG9tZV9jaGFuZ2UNCg0KDQpwcmludChtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbikNCmBgYA0KDQoNCmBgYHtyIGxldHMgYmFyY2hhcnQgaXQgOil9DQptb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbl9sb25nZXIgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbiU+JSBwaXZvdF9sb25nZXIoY29scyA9IGFsbF9vZih0eXBlX29mX3dlYXRoZXIpLG5hbWVzX3RvID0gImRlc2NyaXB0aW9uIix2YWx1ZXNfdG8gPSAidmFsdWUiKSMgJT4lIGZpbHRlcih2YWx1ZSE9MCkNCg0KZGVzY3JpcHRpb25faW1wYWN0X292ZXJhbGwgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbl9sb25nZXIgJT4lIA0KICBmaWx0ZXIodmFsdWUhPTApICU+JSAjbXV0YXRlKHZhbHVlID0gdmFsdWUqbm90X2F0X2hvbWVfY2hhbmdlKSU+JSBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWU+MCx2YWx1ZSpub3RfYXRfaG9tZV9jaGFuZ2UsLXZhbHVlKm5vdF9hdF9ob21lX2NoYW5nZSkpICU+JQ0KICBncm91cF9ieShkZXNjcmlwdGlvbikgJT4lIHN1bW1hcml6ZShpbXBhY3QgPSBtZWFuKG5vdF9hdF9ob21lX2NoYW5nZSkpDQoNCnBsb3RfbHkoZGF0YSA9IGRlc2NyaXB0aW9uX2ltcGFjdF9vdmVyYWxsLHggPSB+ZGVzY3JpcHRpb24seSA9IH5pbXBhY3QsdHlwZT0gImJhciIpDQpgYGANCg0KQW5vdGhlciBhcHByb2FjaA0KDQpgYGB7cn0NCm1hcF92ZWN0b3IgPC0gYygiQ2xlYXIiLCJTdW5ueSIsIkNsb3VkeSIsIkxpZ2h0IiwiTGlnaHQiLCJMaWdodCIsIkxpZ2h0IiwiTGlnaHQiLCJMaWdodCIsIkxpZ2h0IiwiTGlnaHQiLCJNZWRpdW0iLCJDbG91ZHkiLCJMaWdodCIsIkxpZ2h0IiwiSGVhdnkiLCJIZWF2eSIsIkhlYXZ5IiwiTGlnaHQiLCJNZWRpdW0iLCJIZWF2eSIsIkhlYXZ5IiwiTGlnaHQiLCJIZWF2eSIsIkhlYXZ5IiwiSGVhdnkiLCJIZWF2eSIsIkhlYXZ5IiwiSGVhdnkiLCJMaWdodCIsIk1lZGl1bSIsIk1lZGl1bSIsIkxpZ2h0IiwiSGVhdnkiLCJMaWdodCIsIkxpZ2h0IiwiTGlnaHQiLCJMaWdodCIsIkxpZ2h0IiwiSGVhdnkiLCJMaWdodCIsIk1lZGl1bSIsIkhlYXZ5IiwiSGVhdnkiLCJIZWF2eSIpDQpuYW1lcyhtYXBfdmVjdG9yKTwtIHR5cGVfb2Zfd2VhdGhlcg0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25fbG9uZ2VyX21hcHBlZCA9IG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uX2xvbmdlcg0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25fbG9uZ2VyX21hcHBlZCRkZXNjcmlwdGlvbiA9IG1hcF92ZWN0b3JbKG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uX2xvbmdlcl9tYXBwZWQkZGVzY3JpcHRpb24pXQ0KDQpkZXNjcmlwdGlvbl9pbXBhY3RfbWF4ID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25fbG9uZ2VyX21hcHBlZCAlPiUgZ3JvdXBfYnkoeWVhcl93ZWVrKSU+JQ0KICB0b3BfbigxLHZhbHVlKSAlPiUgZ3JvdXBfYnkoZGVzY3JpcHRpb24pICU+JSBzdW1tYXJpemUoaW1wYWN0ID0gbWVhbihub3RfYXRfaG9tZV9jaGFuZ2UpKQ0KDQpkZXNjcmlwdGlvbl9pbXBhY3QgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbl9sb25nZXJfbWFwcGVkICU+JSBncm91cF9ieSh5ZWFyX3dlZWspJT4lDQogIHRvcF9uKDEsdmFsdWUpDQoNCndlZWtfY2FsZW5kZXIgPSBhcy5EYXRlKHNlcShJU09kYXRlKDIwMTQsMSwzKSxJU09kYXRlKDIwMjIsMTIsMSksYnk9IndlZWsiKSkNCndlZWtfY2FsZW5kZXIgPSBkYXRhLmZyYW1lKGRhdGUgPSB3ZWVrX2NhbGVuZGVyKQ0Kd2Vla19jYWxlbmRlciA9IHdlZWtfY2FsZW5kZXIgJT4lIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoeWVhcihkYXRlKSwiLSIsaXNvd2VlayhkYXRlKSkpDQoNCm1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uX2xvbmdlcl9tYXBwZWQgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbl9sb25nZXJfbWFwcGVkICU+JSANCiAgaW5uZXJfam9pbih3ZWVrX2NhbGVuZGVyLGJ5ID0gInllYXJfd2VlayIpDQoNCg0KDQpwbG90X2x5KGRhdGEgPSBkZXNjcmlwdGlvbl9pbXBhY3RfbWF4LHggPSB+ZGVzY3JpcHRpb24seSA9IH5pbXBhY3QsdHlwZT0gImJhciIpDQoNCnByaW50KGRlc2NyaXB0aW9uX2ltcGFjdF9tYXgpDQoNCmBgYA0KDQojIyAqKkFkZCBzdHJpbmdlbmN5IG9mIGNvdmlkIHBvbGljaWVzIHRvIGEgZGF0YSoqDQpgYGB7ciBpbXBvcnR9DQpqc29uID0gZnJvbUpTT04oZmlsZSA9ICJkYXRhLzIwMjItMTAtMDguanNvbiIpDQp1bmxpc3RlZF9qc29uID0gdW5saXN0KGpzb24pDQpgYGANCg0KYGBge3J9DQpkZXVfc3RyaW5nZW5jeSA9IHVubGlzdGVkX2pzb25bZ3JlcCgiREVVLnN0cmluZ2VuY3lfYWN0dWFsIixuYW1lcyh1bmxpc3RlZF9qc29uKSldDQpkYXRlX3N0cmluZ2VuY3kgPSBzYXBwbHkoc3Ryc3BsaXQobmFtZXMoZGV1X3N0cmluZ2VuY3kpLHNwbGl0ID0gIi4iLGZpeGVkID0gVFJVRSksIltbIiwyKQ0KZGZfc3RyaW5nZW5jeSA9IGRhdGEuZnJhbWUoZGF0ZSA9IGRhdGVfc3RyaW5nZW5jeSxzdHJpbmdlbmN5ID0gZGV1X3N0cmluZ2VuY3kpDQpkZl9zdHJpbmdlbmN5ID0gZGZfc3RyaW5nZW5jeSAlPiUgbXV0YXRlKHllYXJfd2VlayA9IHBhc3RlMCh5ZWFyKGRhdGUpLCItIixpc293ZWVrKGRhdGUpKSxzdHJpbmdlbmN5ID0gYXMubnVtZXJpYyhzdHJpbmdlbmN5KSklPiV1bmdyb3VwKCkgJT4lIGdyb3VwX2J5KHllYXJfd2VlaykgJT4lIHN1bW1hcml6ZShzdHJpbmdlbmN5ID0gbWVhbihzdHJpbmdlbmN5KSkNCmBgYA0KDQojIyAqKkluc2VydCBuZXcgZGF0YSBmcm9tIGtleGkgcmVnaW9uKioNCg0KDQpgYGB7cn0NCmRlbWFuZERSVCA9IHJlYWRfZGVsaW0oImRhdGEvYWxsRGVtYW5kQnlEYXRlLmNzdiIpDQpgYGANCg0KDQoNCiMjICoqVGFrZSBhIHBvbGljeSBkYXRhIGludG8gdGFibGUqKg0KIyMgKipTdGF0aXNjdGljYWwgZXZhbHVhdGlvbiBvZiBudWxsIGh5cG90aGVzaXMsIGluZGVwZW5kZW5jeSB0ZXN0cyoqDQojIyAqKlN0YXJ0aW5nIHdpdGggYSBtb2RlbCoqDQoNCg0KDQpBZnRlciBmaXJzdCBkYXRhIHByZXBhcmF0aW9uIGFuZCBhbmFseXNpcywgbGV0J3MgdHJ5IHRvIG1ha2Ugc29tZSBwcmVkaWNpdGlvbnMgYWJvdXQgbm90X2F0X2hvbWUgZHVyYXRpb24gYmFzZWQgb24gcGxvdHMgdGhhdCBzaG93biB1cyBhIG1ham9yIGltcGFjdCBvbiBub3RfYXRfaG9tZSB2YXJpYWJsZSwgbGlrZSAqdGF2ZyosICpzZWFzb24qLCAqZGVzY3JpcHRpb24qLCAqdGF2ZyouIFN0YXJ0aW5nIHdpdGggYSAqbGluZWFyIG1vZGVsKiBhbmQgdXNpbmcgKkluZ29sc3RhZHQgZGF0YSogbGltaXRlZCBieSB5ZWFyIDIwMjAsMjAyMQ0KDQpgYGB7ciBhZGQgYSBzZWFzb259DQpkZXNjcmlwdGlvbl9pbXBhY3QgPSBkZXNjcmlwdGlvbl9pbXBhY3QgJT4lIA0KICBtdXRhdGUoc2Vhc29uID0gaWZlbHNlKG1vbnRoKGRhdGUpICVpbiUgYygxMiwxLDIpLCJ3aW50ZXIiLE5BKSkgJT4lDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDMsNCw1KSwic3ByaW5nIixzZWFzb24pKSAlPiUNCiAgbXV0YXRlKHNlYXNvbiA9IGlmZWxzZShtb250aChkYXRlKSAlaW4lIGMoNiw3LDgpLCJzdW1tZXIiLHNlYXNvbikpICU+JQ0KICBtdXRhdGUoc2Vhc29uID0gaWZlbHNlKG1vbnRoKGRhdGUpICVpbiUgYyg5LDEwLDExKSwiYXV0dW1uIixzZWFzb24pKQ0KZGVzY3JpcHRpb25faW1wYWN0ID0gZGVzY3JpcHRpb25faW1wYWN0ICU+JSBpbm5lcl9qb2luKGRmX3N0cmluZ2VuY3ksIGJ5ID0gInllYXJfd2VlayIpDQoNCmBgYA0KDQojIyAqKkltcG9ydCBob2xpZGF5cyoqDQpgYGB7cn0NCmhvbGlkYXlzMjAyMCA9IHJlYWRfY3N2MigiZGF0YS9Ib2xpZGF5czIwMjAuY3N2IikgJT4lIGRwbHlyOjpzZWxlY3QoMSwyLDMpDQpob2xpZGF5czIwMjEgPSByZWFkX2NzdjIoImRhdGEvSG9saWRheXMyMDIxLmNzdiIpICU+JSBkcGx5cjo6c2VsZWN0KDEsMiwzKQ0KaG9saWRheXMyMDIyID0gcmVhZF9jc3YyKCJkYXRhL0hvbGlkYXlzMjAyMi5jc3YiKSAlPiUgZHBseXI6OnNlbGVjdCgxLDIsMykNCmhvbGlkYXlzID0gcmJpbmQoaG9saWRheXMyMDIwLGhvbGlkYXlzMjAyMSxob2xpZGF5czIwMjIpDQpob2xpZGF5cyA9IGhvbGlkYXlzICU+JSBtdXRhdGUoRW5kRGF0ZVRpbWUxID0gYXMuRGF0ZShtZHlfaG0oRW5kRGF0ZVRpbWUxKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnREYXRlVGltZTEgPSBhcy5EYXRlKG1keV9obShTdGFydERhdGVUaW1lMSkpKQ0KDQpob2xpZGF5X2RheXMgPSBjKHNlcShob2xpZGF5cyRTdGFydERhdGVUaW1lMVsxXSxob2xpZGF5cyRFbmREYXRlVGltZTFbMV0sYnkgPSAiZGF5cyIpKQ0KDQpmb3IoaSBpbiAxOm5yb3coaG9saWRheXMpKXsNCiAgaG9saWRheV9kYXlzID0gYXBwZW5kKGhvbGlkYXlfZGF5cyxzZXEoaG9saWRheXMkU3RhcnREYXRlVGltZTFbaV0saG9saWRheXMkRW5kRGF0ZVRpbWUxW2ldLGJ5ID0gImRheXMiKSkNCn0NCg0KZGZfaG9saWRheXMgPSBkYXRhLmZyYW1lKGRhdGUgPSBob2xpZGF5X2RheXMsaXNIb2xpZGF5ID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KZGVtYW5kRFJUX3dlZWsgPSBkZW1hbmREUlQgJT4lIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoeWVhcihkYXRlKSwiLSIsd2VlayhkYXRlKSkpJT4lIHVuZ3JvdXAoKSAlPiUgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUgc3VtbWFyaXplKG5vUmlkZXMgPSBzdW0obm9SaWRlcyksbm9SZXF1ZXN0cyA9IHN1bShub1JlcXVlc3RzKSxhdmdFdWNsaWRpYW5EaXN0YW5jZV9tID0gbWVhbihhdmdFdWNsaWRpYW5EaXN0YW5jZV9tKSwgYXZnVHJhdmVsVGltZV9zID0gbWVhbihhdmdUcmF2ZWxUaW1lX3MpKQ0KZGVtYW5kX3RhYmxlID0gZGVtYW5kRFJUX3dlZWsgJT4lIGlubmVyX2pvaW4oZGVzY3JpcHRpb25faW1wYWN0LGJ5ID0gInllYXJfd2VlayIpDQoNCmJlc3RfcHJlZCA8LSBkZW1hbmRfdGFibGUgJT4lIHVuZ3JvdXAoKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtbm9SaWRlcywtbGFuZGtyZWlzLC1kZXNjcmlwdGlvbiAsLXllYXJfd2VlaywtZGF0ZSwtdmFsdWUsLXNlYXNvbiwtbm9SZXF1ZXN0cywtYXZnRXVjbGlkaWFuRGlzdGFuY2VfbSwtbm90X2F0X2hvbWVfY2hhbmdlLC1hdmdUcmF2ZWxUaW1lX3MpICU+JQ0KICBtYXBfZGJsKGNvcix5ID0gZGVtYW5kX3RhYmxlJG5vUmlkZXMpICU+JQ0KICBtYXBfZGJsKGFicykgJT4lDQogIHNvcnQoZGVjcmVhc2luZyA9IFRSVUUpIA0KcHJpbnQoYmVzdF9wcmVkKQ0KDQpgYGANCg0KYGBge3IgYmVzdCBwcmVkaWN0b3JzfQ0KYmVzdF9wcmVkIDwtIGRlc2NyaXB0aW9uX2ltcGFjdCAlPiUgdW5ncm91cCgpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1ub3RfYXRfaG9tZV9jaGFuZ2UsLWxhbmRrcmVpcywtZGVzY3JpcHRpb24gLC15ZWFyX3dlZWssLWRhdGUsLXZhbHVlLC1zZWFzb24pICU+JQ0KICBtYXBfZGJsKGNvcix5ID0gZGVzY3JpcHRpb25faW1wYWN0JG5vdF9hdF9ob21lX2NoYW5nZSkgJT4lDQogIG1hcF9kYmwoYWJzKSAlPiUNCiAgc29ydChkZWNyZWFzaW5nID0gVFJVRSkgDQpwcmludChiZXN0X3ByZWQpDQpgYGANCg0KYGBge3IgbW9kZWxyfQ0KDQoNCnRyYWluX2RhdGEgPSBkZXNjcmlwdGlvbl9pbXBhY3QgJT4lIGxlZnRfam9pbihkZl9ob2xpZGF5cywgYnkgPSJkYXRlIikgJT4lIHJlcGxhY2VfbmEobGlzdChpc0hvbGlkYXkgPSBGQUxTRSkpDQoNCg0KZmlyc3RfbW9kZWwgPSBsbShub3RfYXRfaG9tZV9jaGFuZ2UgfiBucyh0YXZnLDEpK2RhdGUrc3RyaW5nZW5jeStkZXNjcmlwdGlvbitpc0hvbGlkYXksDQogICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQptYXNzX21vZGVsID0gTUFTUzo6cmxtKG5vdF9hdF9ob21lX2NoYW5nZSB+IG5zKHRhdmcsMikrZGF0ZStzdHJpbmdlbmN5K2Rlc2NyaXB0aW9uLA0KICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW5fZGF0YSkNCg0KYGBgDQoNCk5lZWQgdG8gY2hlY2sgYXV0by5hcmltYSBhcHByb2FjaA0KDQpgYGB7ciBwcmVkaWN0aW9uc30NCmNvbG9ycyA9IGMoImFjdHVhbCIgPSAiYmx1ZSIsInByZWRpY3RlZCIgPSAicmVkIiwicmVzaWR1YWxzIiA9ICJncmF5NTAiKQ0KbW9kZWwgPSBmaXJzdF9tb2RlbA0KdGVzdF9kYXRhID0gdHJhaW5fZGF0YSAlPiUgYWRkX3ByZWRpY3Rpb25zKG1vZGVsID0gbW9kZWwpICU+JSBhZGRfcmVzaWR1YWxzKG1vZGVsID0gbW9kZWwpDQoNCmdncGxvdGx5KGdncGxvdCh0ZXN0X2RhdGEpICsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGNvbG9yID0gImFjdHVhbCIpKSsNCiAgZ2VvbV9saW5lKGFlcyh4ID0gZGF0ZSx5ID0gcHJlZCxjb2xvciA9ICJwcmVkaWN0ZWQiKSkrDQogIGdlb21fbGluZShhZXMoeCA9IGRhdGUseSA9IHJlc2lkLGNvbG9yID0gInJlc2lkdWFscyIpKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbG9ycykpDQoNCmBgYA0KDQoNCg0KDQpgYGB7cn0NCmJhcnBsb3QgPC0gZ2dwbG90KHRlc3RfZGF0YSwgYWVzKHggPSByZXNpZCApKSsNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSBzdGF0KGRlbnNpdHkpKSxjb2xvdXI9ImJsYWNrIiwgZmlsbD0id2hpdGUiLCBiaW53aWR0aD0yKSsNCiAgZ2VvbV9kZW5zaXR5KCBmaWxsPSIjRkY2NjY2IixhZGp1c3QgPSAxMCxhbHBoYSA9IDAuNSkgDQoNCg0KZ2dwbG90bHkoYmFycGxvdCkNCmBgYA0KDQpgYGB7ciByZXNpZHVhbHMgdmVydGVpbHVuZ30NCmZpdGRpc3RycGx1czo6ZGVzY2Rpc3QodGVzdF9kYXRhJHJlc2lkKQ0KYGBgDQoNCmBgYHtyfQ0Kbm9ybWFsX2Rpc3QgPSBmaXRkaXN0cnBsdXM6OmZpdGRpc3QodGVzdF9kYXRhJHJlc2lkLCJub3JtIikNCnBsb3Qobm9ybWFsX2Rpc3QpDQpgYGANCg0KeH55IHNjYXR0ZXJwbG90LA0KcmVzaWR1YWwgc3RkZXJyb3IsDQprZXhpIHZlcnRlaWx1bmcNCmBgYHtyfQ0KDQpgYGA=